package com.yunquan.ohana.ui.calendar.event

import android.app.Application
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import androidx.databinding.ObservableField
import androidx.lifecycle.LifecycleOwner
import com.google.gson.Gson
import com.yunquan.ohana.R
import com.yunquan.ohana.base.binding.command.BindingAction
import com.yunquan.ohana.base.binding.command.BindingCommand
import com.yunquan.ohana.base.binding.command.BindingConsumer
import com.yunquan.ohana.base.bus.RxBus
import com.yunquan.ohana.base.bus.event.SingleLiveEvent
import com.yunquan.ohana.base.ui.BaseViewModel
import com.yunquan.ohana.db.DBFrames
import com.yunquan.ohana.dialog.LoadingDialog
import com.yunquan.ohana.entity.EventEntity
import com.yunquan.ohana.entity.EventType
import com.yunquan.ohana.entity.FramesDataManager
import com.yunquan.ohana.entity.FramesEntity
import com.yunquan.ohana.entity.LocalFrameEntity
import com.yunquan.ohana.entity.ScheduleModel
import com.yunquan.ohana.http.BaseHttp
import com.yunquan.ohana.http.exception.ApiException
import com.yunquan.ohana.http.observer.BaseObserver
import com.yunquan.ohana.http.transformer.CommonTransformer
import com.yunquan.ohana.ui.calendar.event.add.AddEventActivity
import com.yunquan.ohana.ui.calendar.event.search.EventSearchActivity
import com.yunquan.ohana.utils.DateUtils
import com.yunquan.ohana.utils.ToastUtils
import io.reactivex.rxjava3.disposables.Disposable
import okhttp3.RequestBody.Companion.toRequestBody
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.LocalTime
import java.time.format.DateTimeFormatter
import java.time.temporal.ChronoUnit
import java.util.Locale

class EventViewModel(application: Application) : BaseViewModel(application) {

    val ui = SingleLiveEvent<Int>()

    val title = ObservableField<String>()
    val isShowToday = ObservableField(false)
    var mIsWeek = ObservableField(true)
    var mDeviceId: String? = null
    var userType: Boolean? = null
    var mLocaleDateTime: LocalDateTime = LocalDateTime.now()
    var mWeekCurrentIndex = 0
    var mMonthCurrentIndex = 0
    var mZoneIndex = 0

    var isGoWeek = false

    val onAddEventClick = BindingCommand<Any>(object : BindingAction {
        override fun call() {
            val bundle = Bundle()
            bundle.putString("deviceId", mDeviceId)
            bundle.putBoolean("userType", userType!!)
            startActivityResult(AddEventActivity::class.java, bundle)
        }
    })

    val onGroupChange = BindingCommand(object : BindingConsumer<Int> {
        override fun call(t: Int) {
            if (isGoWeek) return
            mIsWeek.set(t == R.id.rb_week)
            setCurrentDay()
            ui.value = 2
        }
    })

    val onWeekPagerChange = BindingCommand(object : BindingConsumer<Int> {
        override fun call(t: Int) {
            if (isGoWeek) return
            mLocaleDateTime = if (mWeekCurrentIndex > t) {
                mLocaleDateTime.minusWeeks(1)
            } else {
                mLocaleDateTime.plusWeeks(1)
            }
            mWeekCurrentIndex = t
            isShowToday.set(mWeekCurrentIndex != mZoneIndex)
            setCurrentDay()
        }
    })

    val onMonthPagerChange = BindingCommand(object : BindingConsumer<Int> {
        override fun call(t: Int) {
            if (isGoWeek) return
            mLocaleDateTime = if (mMonthCurrentIndex > t) {
                mLocaleDateTime.minusMonths(1)
            } else {
                mLocaleDateTime.plusMonths(1)
            }
            mMonthCurrentIndex = t
            isShowToday.set(mMonthCurrentIndex != mZoneIndex)
            setCurrentDay()
        }
    })

    val onSearchClick = BindingCommand<Any>(object : BindingAction {
        override fun call() {
            val bundle = Bundle()
            bundle.putString("deviceId", mDeviceId)
            startActivityResult(EventSearchActivity::class.java, bundle)
        }
    })

    override fun onCreate(owner: LifecycleOwner) {
        super.onCreate(owner)
        setCurrentDay()
    }

    override fun onEvent(event: EventEntity) {
        super.onEvent(event)
        if (event.type == EventType.EVENT_GO_SHOW_WEEK) {
            val date = event.data as String
            val toDate = LocalDate.parse(date, DateTimeFormatter.ofPattern("yyyy-MM-dd"))
            // 选中日期的当天 00:00:00
            val targetDateTime = LocalDateTime.of(toDate, LocalTime.of(0, 0, 0))

            // 为了保证与周视图的周范围一致（周日-周六），以两者的“周起始（周日）”来计算偏移量
            val (thisWeekStart, _) = DateUtils.getWeekRange(LocalDateTime.now())
            val (targetWeekStart, _) = DateUtils.getWeekRange(targetDateTime)
            val weekOffset = ChronoUnit.WEEKS.between(thisWeekStart, targetWeekStart).toInt()

            // ViewPager2 的当前位置 = 基准位（今天所在页） + 偏移量
            mWeekCurrentIndex = mZoneIndex + weekOffset
            // 更新标题计算所使用的基准时间到选中日期
            mLocaleDateTime = targetDateTime

            // 是否显示“Today”返回按钮
            isShowToday.set(mWeekCurrentIndex != mZoneIndex)

            // 切换到周模式并刷新标题
            mIsWeek.set(true)

            isGoWeek = true

            // 通知 Activity 跳转到对应的周页
            ui.value = 1

            setCurrentDay()
        }
    }

    fun setCurrentDay() {
        val (start, end) = if (mIsWeek.get() == true) {
            DateUtils.getWeekRange(mLocaleDateTime)
        } else {
            DateUtils.getMonthRange(mLocaleDateTime)
        }
        val mFormat = DateTimeFormatter.ofPattern("MMM dd", Locale.getDefault())
        val mDayFormat = DateTimeFormatter.ofPattern("dd", Locale.getDefault())
        val dateStr = if (start.monthValue == end.monthValue) {
            "${start.format(mFormat)} - ${end.format(mDayFormat)}"
        } else {
            "${start.format(mFormat)} - ${end.format(mFormat)}"
        }
        title.set(dateStr)
    }

    fun bindFrames(code: String, eventList: List<String>) {
        val map = mutableMapOf<String, Any>()
        map["qrcode"] = code
        val json = Gson().toJson(map)
        val body = json.toRequestBody(BaseHttp.MEDIA_TYPE_JSON)
        BaseHttp.api.bindDevice(body)
            .compose(CommonTransformer())
            .subscribe(object : BaseObserver<Boolean>() {
                override fun onSubscribe(d: Disposable) {
                    super.onSubscribe(d)
                    LoadingDialog.getInstance().show()
                }

                override fun onSuccess(result: Boolean?) {
                    super.onSuccess(result)
                    if (result == true) {
                        getFrames(eventList)
                    } else {
                        ToastUtils.showShort(R.string.bind_failed)
                        LoadingDialog.getInstance().dismiss()
                    }
                }

                override fun onError(e: ApiException) {
                    super.onError(e)
                    LoadingDialog.getInstance().dismiss()
                }
            })
    }

    fun getFrames(eventList: List<String>) {
        val map = mutableMapOf<String, Any>()
        val json = Gson().toJson(map)
        val body = json.toRequestBody(BaseHttp.MEDIA_TYPE_JSON)
        BaseHttp.api.getUserDeviceList(body)
            .compose(CommonTransformer())
            .subscribe(object : BaseObserver<List<FramesEntity>>() {
                override fun onSuccess(result: List<FramesEntity>?) {
                    super.onSuccess(result)
                    if (result?.isNotEmpty() == true) {
                        FramesDataManager.instance.setData(result)
                        for (i in result.indices) {
                            val e = result[i]
                            val bean = LocalFrameEntity(
                                deviceId = e.id,
                                name = e.name,
                                location = e.roomLocation,
                                online = if (e.online!!) 1 else 0
                            )
                            DBFrames.instance.saveOrUpdate(bean)
                        }
                        val devices = result.filter { it.id != mDeviceId }
                        Handler(Looper.getMainLooper()).postDelayed({
                            syncToDevice(devices, eventList)
                        }, 500)
                        RxBus.getDefault().post(EventEntity(EventType.FRESH_DEVICE_LIST))
                    } else {
                        LoadingDialog.getInstance().dismiss()
                    }
                }

                override fun onError(e: ApiException) {
                    super.onError(e)
                    LoadingDialog.getInstance().dismiss()
                }

                override fun onComplete() {
                    super.onComplete()
                    RxBus.getDefault().post(EventEntity(EventType.UPDATE_DEVICE_LIST_COMPLETE))
                }
            })
    }

    fun syncToDevice(devices: List<FramesEntity>, scheduleIds: List<String>) {
        val syncDeviceList = devices.filter { it.id != mDeviceId }
        if(syncDeviceList.isEmpty()){
            return
        }
        val map = mutableMapOf<String, Any>()
        map["scheduleId"] = scheduleIds
        map["deviceId"] = syncDeviceList.map { it.id }
        val body = Gson().toJson(map).toRequestBody(BaseHttp.MEDIA_TYPE_JSON)
        BaseHttp.api.addSScheduleDeviceRelation(body)
            .compose(CommonTransformer())
            .subscribe(object : BaseObserver<Boolean>() {
                override fun onSubscribe(d: Disposable) {
                    super.onSubscribe(d)
                    if (!LoadingDialog.getInstance().isShowing()) {
                        LoadingDialog.getInstance().show()
                    }
                }

                override fun onSuccess(result: Boolean?) {
                    super.onSuccess(result)
                    if (result == true) {
                        ToastUtils.showShort(R.string.synchronization_is_successful)
                    } else {
                        ToastUtils.showShort(R.string.synchronization_failed)
                    }
                }

                override fun onComplete() {
                    super.onComplete()
                    LoadingDialog.getInstance().dismiss()
                }
            })
    }

}